package engine.game.hud;

import engine.entities.Entity;
import engine.entities.Mob;
import engine.entities.Player;
import engine.game.Game;
import engine.game.combat.ability.Ability;
import engine.game.combat.ability.universal.AetherEssence;
import engine.game.combat.ability.champion.PowerAttack;
import engine.game.gfx.Screen.ScreenSelectFilter;
import engine.game.gfx.Screen.ScreenSelectProcessor;
import engine.game.level.Level;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;

public class Hotbar extends JPanel {

    private static final long serialVersionUID = 1L;

    private Game game;

    private JPanel statusPanel;
    private JPanel actionPanel;
    private JPanel commonActionPanel;
    private JPanel quickPanel;
    private JPanel wheelPanel;

    private JPanel moveButtonWrapper;
    private JPanel attackButtonWrapper;
    private JPanel cancelButtonWrapper;

    private JButton moveButton;
    private JButton attackButton;
    private JButton cancelButton;
    private AttackButtonListener atkListener = new AttackButtonListener();
    private MoveButtonListener movListener = new MoveButtonListener();
    private CancelButtonListener canListener = new CancelButtonListener();

    private AbilityButton[][] quickButtons = new AbilityButton[3][10];
    private Ability[][] abilities = new Ability[3][10];
    private JPanel[][] quickWrappers = new JPanel[3][10];

    private JButton[] wheelButtons = new JButton[4];
    private JPanel[] wheelWrappers = new JPanel[4];

    private ResourceBar healthBar;
    private ResourceBar manaBar;
    private ResourceBar staminaBar;
    private ResourceBar aetherBar;

    private Ability curAbility;
    private Mob curAbilityCaster;
    private Mob curAbilityTargetMob;
    private Point curAbilityTargetPoint;

    public Hotbar(Game game) {

        this.game = game;
        
        abilities[0][0] = new AetherEssence();
        abilities[0][1] = new PowerAttack();
        
        actionPanel = new JPanel();

        quickPanel = new JPanel(new GridLayout(3, 10));
        for (int i = 0; i < quickButtons.length; i++) {
            for (int j = 0; j < quickButtons[0].length; j++) {
                if (abilities[i][j] == null) {
                quickButtons[i][j] = new AbilityButton(((i * 10) + (j + 1)) + "");
                }
                else {
                    quickButtons[i][j] = new AbilityButton(abilities[i][j]);
                }
                quickWrappers[i][j] = new JPanel();
                quickWrappers[i][j].setLayout(new BorderLayout());
                quickWrappers[i][j].add(quickButtons[i][j], BorderLayout.CENTER);
                quickWrappers[i][j].setBorder(BorderFactory.createEmptyBorder(3, 5, 2, 0));
                quickPanel.add(quickWrappers[i][j]);
            }
        }
        quickPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 3, 5));

        wheelPanel = new JPanel(new GridLayout(3, 3));
        for (int i = 0; i < 4; i++) {
            wheelButtons[i] = new JButton("P " + (i + 1));
            wheelWrappers[i] = new JPanel();
            wheelWrappers[i].setLayout(new BorderLayout());
            wheelWrappers[i].add(wheelButtons[i], BorderLayout.CENTER);
            wheelWrappers[i].setBorder(BorderFactory.createEmptyBorder(3, 5, 2, 0));
        }
        wheelPanel.add(Box.createRigidArea(new Dimension(1, 1)));
        wheelPanel.add(wheelWrappers[0]);
        wheelPanel.add(Box.createRigidArea(new Dimension(1, 1)));
        wheelPanel.add(wheelWrappers[1]);
        wheelPanel.add(Box.createRigidArea(new Dimension(1, 1)));
        wheelPanel.add(wheelWrappers[2]);
        wheelPanel.add(Box.createRigidArea(new Dimension(1, 1)));
        wheelPanel.add(wheelWrappers[3]);
        wheelPanel.add(Box.createRigidArea(new Dimension(1, 1)));
        wheelPanel.setBorder(BorderFactory.createEmptyBorder(2, 0, 3, 5));

        actionPanel.setLayout(new GridBagLayout());

        commonActionPanel = new JPanel();
        commonActionPanel.setLayout(new GridLayout(3, 1));

        moveButton = new JButton("Move");
        moveButton.addActionListener(movListener);
        moveButtonWrapper = new JPanel();
        moveButtonWrapper.setLayout(new BorderLayout());
        moveButtonWrapper.add(moveButton, BorderLayout.CENTER);
        moveButtonWrapper.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));

        attackButton = new JButton("Attack");
        attackButton.addActionListener(atkListener);
        attackButtonWrapper = new JPanel();
        attackButtonWrapper.setLayout(new BorderLayout());
        attackButtonWrapper.add(attackButton, BorderLayout.CENTER);
        attackButtonWrapper.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
        
        cancelButton = new JButton("X");
        cancelButton.addActionListener(canListener);
        cancelButtonWrapper = new JPanel();
        cancelButtonWrapper.setLayout(new BorderLayout());
        cancelButtonWrapper.add(cancelButton, BorderLayout.CENTER);
        cancelButtonWrapper.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));

        commonActionPanel.add(moveButtonWrapper);
        commonActionPanel.add(attackButtonWrapper);
        commonActionPanel.add(cancelButtonWrapper);

        GridBagConstraints c = new GridBagConstraints();
        c.gridx = 0;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 0.3;
        c.weighty = 0.3;
        c.anchor = GridBagConstraints.LINE_START;
        actionPanel.add(commonActionPanel, c);

        c = new GridBagConstraints();
        c.gridx = 1;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 1.0;
        c.anchor = GridBagConstraints.LINE_START;
        actionPanel.add(quickPanel, c);

        c = new GridBagConstraints();
        c.gridx = 2;
        c.gridy = 0;
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 0.2;
        c.weighty = 0.2;
        c.anchor = GridBagConstraints.LINE_START;
        actionPanel.add(wheelPanel, c);

        healthBar = new ResourceBar(100, Color.RED);
        healthBar.setTextColor(Color.BLACK);
        staminaBar = new ResourceBar(100, Color.GREEN);
        staminaBar.setTextColor(Color.BLACK);
        manaBar = new ResourceBar(100, Color.BLUE);
        manaBar.setTextColor(Color.WHITE);
        aetherBar = new ResourceBar(100, Color.DARK_GRAY);
        aetherBar.setTextColor(Color.WHITE);

        statusPanel = new JPanel();
        statusPanel.setLayout(new GridLayout(1, 4));
        statusPanel.add(healthBar);
        statusPanel.add(staminaBar);
        statusPanel.add(manaBar);
        statusPanel.add(aetherBar);

        this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
        this.add(statusPanel);
        this.add(actionPanel);
        this.revalidate();

        updatePlayerResources();
    }

    private void updatePlayerResources() {
        Player player;
        if ((player = game.getActivePlayer()) != null) {
            healthBar.setValue(player.getHealth().getValue());
            healthBar.setMaxValue(player.getHealth().getMaxValue());
            staminaBar.setValue(player.getStamina().getValue());
            staminaBar.setMaxValue(player.getStamina().getMaxValue());
            manaBar.setValue(player.getMana().getValue());
            manaBar.setMaxValue(player.getMana().getMaxValue());
            aetherBar.setValue(player.getAether().getValue());
            aetherBar.setMaxValue(player.getAether().getMaxValue());
        }
    }

    public void render() {
        updatePlayerResources();
        healthBar.repaint();
        manaBar.repaint();
        staminaBar.repaint();
        aetherBar.repaint();
    }

    public static class ResourceBar extends JPanel {

        private final int EXTERNAL_BORDER_WIDTH = 3;
        private final int BORDER_WIDTH = 3;
        private final Color BORDER_COLOR = Color.BLACK;
        private final Color BACK_COLOR = Color.GRAY;

        private final Font labelFont = new Font("Arial", Font.PLAIN, 10);

        private int maxValue;
        private int value;

        private Color color;
        private Color textColor;

        public ResourceBar(int maxValue, Color color) {
            this.maxValue = maxValue;
            this.color = color;

            this.textColor = Color.BLACK;
            this.value = maxValue;
            this.setBorder(BorderFactory.createEmptyBorder(EXTERNAL_BORDER_WIDTH, EXTERNAL_BORDER_WIDTH, EXTERNAL_BORDER_WIDTH, EXTERNAL_BORDER_WIDTH));
        }

        public void setValue(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }

        public void setMaxValue(int maxValue) {
            this.maxValue = maxValue;
        }

        public int maxValue() {
            return maxValue;
        }

        public void setTextColor(Color color) {
            this.textColor = color;
        }

        @Override
        public void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2D = (Graphics2D) g.create();
            g2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

            double percent = ((double) value) / ((double) maxValue);
            int width = getWidth() - getInsets().left - getInsets().right;
            int height = getHeight() - getInsets().top - getInsets().bottom;
            int barWidth = (int) Math.round((width - (BORDER_WIDTH * 2)) * percent);
            int barHeight = height - (BORDER_WIDTH * 2);
            int outerCurveRadius = height / 2;
            int innerCurveRadius = barHeight / 2;

            g2D.setColor(BORDER_COLOR);
            g2D.fillRoundRect(getInsets().left, getInsets().top, width, height, outerCurveRadius, outerCurveRadius);
            g2D.setColor(BACK_COLOR);
            g2D.fillRoundRect(getInsets().left + BORDER_WIDTH, getInsets().top + BORDER_WIDTH, width - (BORDER_WIDTH * 2), height - (BORDER_WIDTH * 2), innerCurveRadius, innerCurveRadius);
            g2D.setColor(color);
            g2D.fillRoundRect(getInsets().left + BORDER_WIDTH, getInsets().top + BORDER_WIDTH, barWidth, barHeight, innerCurveRadius, innerCurveRadius);

            g2D.setColor(textColor);
            g2D.setFont(labelFont);
            FontMetrics metrics = g2D.getFontMetrics();
            String centerText = value + "/" + maxValue;
            int centerX = getWidth() / 2;
            int centerY = getHeight() / 2;
            int stringStartX = centerX - (metrics.stringWidth(centerText) / 2);
            int stringStartY = centerY + (metrics.getHeight() / 4);
            g2D.drawString(centerText, stringStartX, stringStartY);

            g2D.dispose();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(100, 24);
        }

        @Override
        public Dimension getMinimumSize() {
            return new Dimension(1, 20);
        }

    }

    public class AbilityButton extends JButton {

        private Ability ability;

        public AbilityButton(String s) {
            super(s);
            this.ability = null;
        }

        public AbilityButton(Ability ability) {
            super(ability.getIcon());
            this.ability = ability;
            this.setToolTipText(ability.getToolTipString());
            this.addActionListener(new AbilityButtonListener());
        }

        public Ability getAbility() {
            return ability;
        }

        public void setAbility(Ability ability) {
            throw new UnsupportedOperationException();
        }

        private class AbilityButtonListener implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent e) {
                Object s = e.getSource();
                if (!(s instanceof AbilityButton)) {
                    return;
                }
                AbilityButton source = (AbilityButton) s;
                Ability ability = source.getAbility();

                Player player = game.getActivePlayer();
                curAbility = ability;
                curAbilityCaster = player;
                curAbilityTargetMob = null;
                curAbilityTargetPoint = null;

                if (player.isInCombat() && player.combatTurnOver()) {
                    return;
                }
                
                if (!curAbility.canPayCosts(curAbilityCaster)) {
                    return;
                }

                if (!ability.requiresMobTarget() && !ability.requiresPointTarget()) {
                    ability.activateAbility(player, null, null);
                }
                else if (ability.requiresMobTarget()) {
                    if (ability.requiresPointTarget()) {
                        game.startSelection(curAbility.getMobFilter(), new AbilityMobProcessor(true));
                    }
                    else {
                        game.startSelection(curAbility.getMobFilter(), new AbilityMobProcessor(false));
                    }
                }
                else {
                    game.startSelection(curAbility.getPointFilter(), new AbilityPointProcessor());
                }
            }
        }
    }

    private class AttackButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            game.startSelection(new AttackFilter(game), new AttackProcessor(game.getActivePlayer()));
        }

    }

    private class MoveButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            Player player = game.getActivePlayer();

            if (player.isInCombat() && player.combatTurnOver()) {
                return;
            }

            // Determine legal squares to move to
            // wait for screen to select a target square
            // walk to the square
        }

    }

    private class CancelButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            game.stopSelection();
        }
        
    }
    
    public static class AttackFilter implements ScreenSelectFilter {

        private Game game;

        public AttackFilter(Game game) {
            this.game = game;
        }

        @Override
        public boolean acceptPoint(Level level, Point p) {
            Entity ent = level.entityAt(p);
            if ((ent == null) || !(ent instanceof Mob)) {
                return false;
            }
            Mob mob = (Mob) ent;
            if (mob.isFriendsWith(game.getActivePlayer())) {
                return false;
            }
            Point player = game.getActivePlayer().getCoords();
            if (p.x == player.x) {
                return Math.abs(p.y - player.y) == 1;
            }
            else if (p.y == player.y) {
                return Math.abs(p.x - player.x) == 1;
            }
            return false;
        }

    }

    private class AttackProcessor implements ScreenSelectProcessor {

        private Player attacker;

        public AttackProcessor(Player attacker) {
            this.attacker = attacker;
        }

        @Override
        public void processSelection(Level level, Point p) {
            Entity ent = level.entityAt(p);
            if (ent == null || !(ent instanceof Mob)) {
                return;
            }
            attacker.attack((Mob) ent);
        }

    }

    private class AbilityMobProcessor implements ScreenSelectProcessor {

        public boolean pointAfter;

        public AbilityMobProcessor(boolean pointAfter) {
            this.pointAfter = pointAfter;
        }

        @Override
        public void processSelection(Level level, Point p) {
            curAbilityTargetMob = (Mob) level.entityAt(p);
            if (pointAfter) {
                game.startSelection(curAbility.getPointFilter(), new AbilityPointProcessor());
            }
            else {
                curAbilityTargetPoint = p;
                curAbility.activateAbility(curAbilityCaster, curAbilityTargetMob, curAbilityTargetPoint);
            }
        }

    }

    private class AbilityPointProcessor implements ScreenSelectProcessor {

        @Override
        public void processSelection(Level level, Point p) {
            curAbilityTargetPoint = p;
            curAbility.activateAbility(curAbilityCaster, curAbilityTargetMob, curAbilityTargetPoint);
        }

    }

}
